#ifndef GAMEENGINE_CORE_FACTORY_FACTORY_MANAGER_HPP
#define GAMEENGINE_CORE_FACTORY_FACTORY_MANAGER_HPP

#include <GameEngine/Core/Factory/factory_builder.h>
#include <assert.h>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif


//========================================================


//! Default constructor
template <class T>
FactoryManager<T>::FactoryManager() {
	// Ensure type T has RTTI support
	int rtti = T::rtti();
}

//! Destructor
template <class T>
FactoryManager<T>::~FactoryManager() {
	clear();
}

//! Create an object using the factory creator bound to class C
/*!
 @return The created object. May be nil if there is no factory builder
         bound to class C.
 */
template <class T>
template <class C>
T* FactoryManager<T>::create_object() {
	// Try to find a factory builder bound to class C
	std::map<GEString,FactoryBuilder*>::iterator it = bindings_.find( typeid(C).name() );
	if ( it == bindings_.end() ) return nil;

	// Create the object using the factory builder's capabilities
	FactoryBuilder::Ptr object = it->second->create_object();

	// Return the object
	return (T*)object;
}

//! Bind given factory builder to the class C
/*!
 *  Class C must derive from class T and have RTTI support that, directly or
 *  indirectly, relates to class T. If this requisite is not met, the bind()
 *  function will always return false.
 *
 *  Memory allocated for the factory builder should have been referenced to
 *  the memory manager.
 *
 @return True if binding could be done, false otherwise. If a binding previously
         existed, it is removed.
 */
template <class T>
template <class C>
bool FactoryManager<T>::bind(FactoryBuilder* builder) {
	// Check RTTI type
	if ( C::is_a<T>() == false ) return false;

	// Unbind previous factory builder, if any
	unbind<C>();

	// Bind new factory builder
	bindings_[typeid(C).name()] = builder;
	return true;
}

//! Unbind factory builder associated to the class C, if any
/*!
 @return True if binding could be removed, false otherwise
 */
template <class T>
template <class C>
bool FactoryManager<T>::unbind() {
	// Try to find a factory builder bound to class C
	std::map<GEString,FactoryBuilder*>::iterator it = bindings_.find( typeid(C).name() );
	if ( it == bindings_.end() ) return false;

	// Erase factory builder
	MemoryManager::instance()->desallocate(it->second);
	bindings_.erase(it);
	return true;
}

//! Clear all factory builders bound to this factory manager
template <class T>
void FactoryManager<T>::clear() {
	for ( std::map<GEString,FactoryBuilder*>::iterator it = bindings_.begin(); it != bindings_.end(); ++it ) {
		MemoryManager::instance()->desallocate(it->second);
	}
	bindings_.clear();
}


//========================================================


#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#endif
